#ifndef __CO_CONNECTION_H__
#define __CO_CONNECTION_H__

#include <iostream>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

class Connection
{
public:
    static Connection* CreateListener()
    {
        struct sockaddr_in stLocalAddr;

        int iFd = socket(AF_INET, SOCK_STREAM, 0);
        if (iFd == -1)
        {
            return nullptr;
        }

        stLocalAddr.sin_family = AF_INET;
        stLocalAddr.sin_addr.s_addr = INADDR_ANY;
        stLocalAddr.sin_port = htons(8888);
        int iErr = bind(iFd, (const struct sockaddr*)&stLocalAddr, sizeof(stLocalAddr));
        if (iErr == -1)
        {
            close(iFd);
            return nullptr;
        }

        iErr = listen(iFd, 8);
        if (iErr == -1)
        {
            close(iFd);
            return nullptr;
        }

        Connection* pobjConn = new (std::nothrow) Connection(iFd, true);
        if (pobjConn == nullptr)
        {
            close(iFd);
            return nullptr;
        }
        
        return pobjConn;
    }

public:
    Connection(int iSock, bool bIsListener):
        m_iSock(iSock),
        m_bIsListener(bIsListener)
    {

    }

    int SetNoBlock()
    {
        int iFlag;

        if (m_iSock == -1)
        {
            return -1;
        }

        iFlag = fcntl(m_iSock, F_GETFL, 0);
        if (iFlag != -1)
        {
            return fcntl(m_iSock, F_SETFL, iFlag | O_NONBLOCK);
        }

        return -1;
    }

    bool IsListener()
    {
        return m_bIsListener;
    }

    Connection* Accept()
    {
        int iErr;
        int iAcceptFd;
        struct sockaddr_in stPeerAddr;
        socklen_t uiAddrLen = sizeof(stPeerAddr);

        iAcceptFd = accept(m_iSock, (struct sockaddr*)&stPeerAddr, &uiAddrLen);
        if (iAcceptFd == -1)
        {
            return nullptr;
        }

        Connection* pobjAConn = new (std::nothrow) Connection(iAcceptFd, false);
        if (pobjAConn == nullptr)
        {
            close(iAcceptFd);
            return nullptr;
        }

        pobjAConn->SetNoBlock();

        return pobjAConn;
    }

    void Close()
    {
        if (m_iSock != -1)
        {
            close(m_iSock);
            m_iSock = -1;
        }
    }

    int GetSock()
    {
        return m_iSock;
    }

private:
    int m_iSock = -1;
    bool m_bIsListener = false;
};


void AcceptCoroutine(void* pScheduler, uint32_t uiCoId);
void ReadCoroutine(void* pScheduler, uint32_t uiCoId);
void WriteCoroutine(void* pScheduler, uint32_t uiCoId);

#endif //